/*
 * zoj 3998 . Yet Another Data Structure Problem
 * 题目网址： https://zoj.pintia.cn/problem-sets/91827364500/problems/91827370149
 * 题解: 
 * 		操作1,区间[l, r]所有数乘以v，则区间data[MULTIPLY]需要乘以v的(r-l+1)次方
 * 		操作2,区间[l, r]所有数取k的次幂,区间data[MULTIPLY]也取k的次幂即可,根据费马小定理，指数需要对(MOD-1)取模
 */

#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;

/*
 * 模板说明
 * seg_data_type： 线段树维护的信息的数据类型
 * SegNode::data_init 节点数据初始化
 * SegNode::leaf_init 叶子节点数据初始化
 * SegNode::push_down [l,r]区间维护的数据往下分配给子节点
 * SegNode::push_up 据子节点的信息更新区间[l,r]维护的信息
 * SegNode::query 查询或更新子树
 * SegNode::merge_val 合并从子树查询到的结果
 * SegTree::start_pos 区间起始位置0或者1
 */

typedef long long seg_data_type; 

#define MOD 1000000007
inline seg_data_type data_transform(seg_data_type d){
	return d % MOD;
}

/*
 * 求a的e次方
 */
seg_data_type quick_power(seg_data_type a, seg_data_type e){
	seg_data_type p = 1;
	while(e){
		if(e & 1){
			p = data_transform(p * a);
		}
		a = data_transform(a * a);
		e >>= 1;
	}
	return p;
}

//节点类型
enum NODE_DATA{
	FACTOR, 	//乘法因子
	EXPONENT, 	//幂指数
	MULTIPLY, 	//间所有元素的乘积
	NODE_DATA_NUM
};

class SegTree;
class SegNode{
	friend SegTree;
private:
	seg_data_type 		data[NODE_DATA_NUM];
	SegNode * 			children[2]; 	//子节点
	int l; 				//区间左边界
	int r; 				//区间右边界
public:

	void data_init(){
		data[FACTOR] =  data[EXPONENT] = data[MULTIPLY] = 1;
	}

	void leaf_init(seg_data_type d){
		data[MULTIPLY] = d;
	}

	void push_down(){
		for(int i = 0; i < 2; i++){
			if(children[i]){
				if(data[EXPONENT] > 1){
					children[i]->data[FACTOR] = quick_power(children[i]->data[FACTOR], data[EXPONENT]);
					children[i]->data[EXPONENT] = (children[i]->data[EXPONENT] * data[EXPONENT]) % (MOD - 1);
					children[i]->data[MULTIPLY] = quick_power(children[i]->data[MULTIPLY], data[EXPONENT]);
				}
				if(data[FACTOR] > 1){
					children[i]->data[FACTOR] = data_transform(children[i]->data[FACTOR] * data[FACTOR]);
					children[i]->data[MULTIPLY] = data_transform(children[i]->data[MULTIPLY] * quick_power(data[FACTOR], children[i]->len()));
				}
			}
		}
		data[EXPONENT] = data[FACTOR] = 1;
	}

	//根据子节点的信息更新信息
	void push_up(){
		data[MULTIPLY] = data_transform(children[0]->data[MULTIPLY] * children[1]->data[MULTIPLY]);
	}

	
	seg_data_type query(int op, int k){
		seg_data_type v = 1;
		// 更新节点的data[MULTIPLY]
		if(op == 1){
			data[FACTOR] = data_transform(data[FACTOR] * k);
			data[MULTIPLY] = data_transform(data[MULTIPLY] * quick_power(k, len()));
		}else if(op == 2){
			data[EXPONENT] = (data[EXPONENT] * k) % (MOD - 1);
			data[FACTOR] = quick_power(data[FACTOR], k);
			data[MULTIPLY] = quick_power(data[MULTIPLY], k);
		}else if(op == 3){
			v = data[MULTIPLY];
		}
		return v;
	}
	
	void merge_val(int op, seg_data_type& val, seg_data_type sub_tree_val){
		if(op == 3){
			val = data_transform(val * sub_tree_val);
		}
	}
	SegNode(){
		init();
	}

	void init(){
		l = r = 0;
		children[0] = children[1] = NULL;
		data_init();
	}

	bool is_leaf(){
		return l == r;
	}

	bool not_leaf(){
		return !is_leaf();
	}

	//判断线段的区间是否完全在区间[l, r]里
	bool inside(int l, int r){
		return l <= this->l && this->r <= r;
	}

	//判断线段的区间是否和区间[l, r]完全不相交
	bool no_intersect(int l, int r){
		return this->r < l || r < this->l;
	}

	bool intersect(int l, int r){
		return !no_intersect(l, r);
	}

	int len(){
		return r - l + 1;
	}
	


	void output(){
		printf("[%d,%d] data[MULTIPLY]=%ld data[FACTOR]=%ld data[EXPONENT]=%ld\n", l, r, data[MULTIPLY], data[FACTOR], data[EXPONENT]);
	}

	//中点
	int mid(){
		return (l + r) >> 1;
	}

};

class SegTree{
private:
	int start_pos;  //区间起始位置，0或者1
	vector<SegNode> tree;
	int node_num; 	//节点个数
public:
	SegTree(int init_num){
		start_pos = 1;
		tree.resize(init_num);
	}

	//初始化线段树
	void init_tree(int *data, int n){
		//线段树节点的个数为2*-1
		if(2 * n - 1 > tree.size()){
			tree.resize(2 * n - 1);
		}
		node_num = 0;
		init_seg(&tree[node_num++], data, n, start_pos, n - 1 + start_pos);
	}

	//初始化区间
	void init_seg(SegNode *node, int *data, int n, int l, int r){
		node->init();
		node->l = l;
		node->r = r;
		if(l == r){
			node->leaf_init(data[l - start_pos]);
		}else{
			int mid = node->mid();
			for(int i = 0; i < 2; i ++){
				node->children[i] = &tree[node_num++];
			}
			// mid属于左子树
			init_seg(node->children[0], data, n, l, mid);
			init_seg(node->children[1], data, n, mid + 1, r);
			node->push_up();
		}
	}

	SegNode* root(){
		return &tree[0];
	}


	seg_data_type query_tree(int op, int l, int r, int k){
		return query(root(), op, l, r, k);
	}

	seg_data_type query(SegNode *node, int op, int l, int r, int k){
		seg_data_type v = 1;
		if(node->inside(l, r)){
			v = node->query(op, k);
		}else if(node->not_leaf() && node->intersect(l, r)){
			node->push_down();
			//区间相交往下降 
			int mid = node->mid();
			if(l <= mid){
				node->merge_val(op, v, query(node->children[0], op, l, min(mid, r), k));
			}
			if(r > mid){
				node->merge_val(op, v, query(node->children[1], op, max(mid + 1, l), r, k));
			}
			node->push_up();
		}
		return v;
	}

	void output(){
		for(int i = 0; i < node_num; i++){
			tree[i].output();
		}
	}

};

int main(int argc, char* argv[])
{
	const int NUM = 100000;
	int data[NUM];
	int c, t;
	int i, n, q, op, l, r, k;
	SegTree seg_tree(2 * NUM);
	scanf("%d", &t);
	for(c = 0; c < t; c++){
		//线段树节点的个数为2*n-1
		scanf("%d%d", &n, &q);
		for(i = 0; i < n; i++){
			scanf("%d", &data[i]);
		}
		seg_tree.init_tree(data, n);
		// seg_tree.output();
		for(i = 0; i < q; i++){
			scanf("%d%d%d", &op, &l, &r);
			if(op == 1 || op == 2){
				scanf("%d", &k);
				seg_tree.query_tree(op, l, r, k);
			}else if(op == 3){
				printf("%ld\n", seg_tree.query_tree(op, l, r, 0));
			}
			// seg_tree.output();
		}
	}
    return 0;
}
